#pragma once

#include "base/uncopyable.hpp"
#include "event/loop.hpp"
#include "network/sockaddr.hpp"
#include "network/socket_fd.hpp"

namespace ibox::network {
class TcpConnection;

using namespace ibox::base;
class TcpConnector : public Uncopyable {
public:
    explicit TcpConnector(event::Loop *loop);
    virtual ~TcpConnector();

    //! 状态
    enum class State {
        None,           //!< 未初始化
        Inited,         //!< 空闲未连接
        ReconnectDelay, //!< 重连等待
        Connecting,     //!< 连接中
    };

    //! 函数类型定义
    using ConnectedCallback = std::function<void(TcpConnection *)>;
    using ConnectFailCallback = std::function<void()>;
    using ReconnectDelayCalc = std::function<int(int)>;

    //! 必需设置项
    void initialize(const SockAddr &server_addr); //!< 设置服务端地址
    void
    set_connected_callback(const ConnectedCallback &cb); //!< 设置连接成功的回调

    //! 非必需设置项
    void set_try_times(int try_times); //!< 设置连接尝试次数
    void set_connect_fail_callback(
        const ConnectFailCallback &cb); //!< 设置连接失败的回调
    void set_reconnect_delay_calc_func(
        const ReconnectDelayCalc &func); //!< 设置用户自定义的重连延迟策略

    bool start(); //!< 开始连接
    void stop();  //!< 停止连接

    void cleanup(); //!< 清理

    State state() const { return m_state; }

protected:
    virtual SocketFd create_socket(SockAddr::Type addr_type) const;
    virtual int connect(SocketFd sock_fd, const SockAddr &addr) const;

    void check_setting_and_try_enter_idle_state();

    void enter_connecting_state();      //!< 进入连接状态的操作
    void exit_connecting_state();       //!< 退出连接状态的操作
    void enter_reconnect_delay_state(); //!< 进入重连等待状态的操作
    void exit_reconnect_delay_state();  //!< 退出重连等待状态的操作

    void on_connect_fail();
    void on_socket_writable(); //!< 当连接成功时的处理
    void on_delay_timeout();   //!< 当等待延时到期后的处理

private:
    event::Loop *m_loop = nullptr;

    State m_state = State::None; //! 当前状态

    //! 设置项
    SockAddr m_server_addr; //!< 目标服务器地址
    ConnectedCallback m_connected_cb;
    ConnectFailCallback m_connect_fail_cb;
    ReconnectDelayCalc m_reconn_delay_calc_func;
    int m_try_times = 0; //!< 尝试连接次数，默认一直尝试

    SocketFd m_sock_fd;

    std::unique_ptr<event::EpollFdEvent>
        m_write_fd{}; //!< connect过程的结果监听事件
    std::unique_ptr<event::TimerEvent> m_delay_timer{}; //!< 重连等待延时定时器

    int m_conn_fail_times = 0; //! 连续 connect 失败计数
};

} // namespace ibox::network
